package com.disapand.dingdang.exception

import com.disapand.dingdang.common.ResponseEnum
import com.disapand.dingdang.vo.ResponseVO
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MissingServletRequestParameterException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.multipart.support.MissingServletRequestPartException
import java.util.*
import javax.servlet.http.HttpServletRequest
import javax.validation.ConstraintViolationException

/**
 * @author disapand
 * @date 2022/2/23 - 20:22 星期三
 */
@RestControllerAdvice
class ExceptionHandle {

    /**
     * 以 POST 方式提交 JSON 参数时，参数缺失的异常
     *
     * @param httpMessageNotReadableException
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException::class)
    fun httpMessageNotReadableExceptionHandle(httpMessageNotReadableException: HttpMessageNotReadableException): ResponseVO {
        return when (val rootCause = httpMessageNotReadableException.rootCause) {
            is MissingKotlinParameterException -> if (rootCause.message?.contains(
                    "non-nullable",
                    ignoreCase = true
                ) == true
            ) {
                val msg = "参数 ${rootCause.parameter.name} 不能为空"
                ResponseVO.ofError(ResponseEnum.PARAMS_ERROR, msg)
            } else {
                println(rootCause.message)
                ResponseVO.ofError(ResponseEnum.PARAMS_ERROR, rootCause.message ?: "")
            }
            null -> {
                println(httpMessageNotReadableException.message)
                ResponseVO.ofError(httpMessageNotReadableException.message ?: "")
            }
            else -> {
                println(rootCause.message)
                ResponseVO.ofSuccess()
            }
        }
    }

    /**
     * 自定义异常捕获
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(DDException::class)
    fun ddException(exception: DDException):ResponseVO {
        return ResponseVO(exception.code, exception.msg)
    }

    /**
     * requestParameter 传参方式参数缺失
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException::class)
    fun missingRequestParameterException(exception: MissingServletRequestParameterException): ResponseVO {
        return ResponseVO.ofError(ResponseEnum.PARAMS_ERROR, "参数 ${exception.parameterName} 不能为空")
    }

    /**
     * requestPart 传参方式参数缺失
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(MissingServletRequestPartException::class)
    fun missingRequestPartException(exception: MissingServletRequestPartException): ResponseVO {
        return ResponseVO.ofError(ResponseEnum.PARAMS_ERROR, "参数 ${exception.requestPartName} 不能为空")

    }

    /**
     * 请求方法不支持
     *
     * @param request
     * @param exception
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException::class)
    fun methodNotSupportException(
        request: HttpServletRequest,
        exception: HttpRequestMethodNotSupportedException
    ): ResponseVO {
        return ResponseVO.ofError(
            ResponseEnum.METHOD_NOT_SUPPORT,
            "当前发起请求的方法是[${exception.method}], 接口[${request.requestURI}]只支持${Arrays.toString(exception.supportedMethods)}方法"
        )
    }

    /**
     * 参数校验错误
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(ConstraintViolationException::class)
    fun constraintViolationException(exception: ConstraintViolationException): ResponseVO {
        val constraintViolations = exception.constraintViolations
        val iterator = constraintViolations.iterator()

        val result = iterator.next()
        val path = result.propertyPath
        val property = path.toString().split(".").last()
        return ResponseVO.ofError(ResponseEnum.PARAMS_ERROR, "参数 $property 校验失败：${result.message}")
    }
}